﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SpeakupBackend.Classes;
using System.ComponentModel;
using System.Runtime.Serialization;
using System.Diagnostics;

namespace SpeakupBackend.Themes
{
    [Serializable]
    public abstract class ThemeBase: INotifyPropertyChanged
    {
        /// <summary>
        /// Return true if class is of current theme.
        /// Return false if class is not of current theme.
        /// </summary>
        /// <param name="cl"></param>
        /// <returns></returns>
        public abstract bool IsCompatibleClass(Class cl);

        /// <summary>
        /// Set of classes that have current theme assigned to them.
        /// </summary>        
        HashSet<Class> m_classes = new HashSet<Class>();

        public ICollection<Class> Classes
        {
            get
            {
                return m_classes.ToList().AsReadOnly();
            }
        }

        /// <summary>
        /// Add class that has current theme
        /// </summary>
        /// <param name="cl"></param>
        public bool AddClass(Class cl)
        {
            if (!IsCompatibleClass(cl))
            {
                throw new ArgumentException("Invalid Class: class is not assignable for this theme.");
            }

            if (m_classes.Add(cl))
            {
                cl.PropertyChanged += OnClassPropertyChanges;

                if (cl.CurrentStatus == ClassStatus.Attended)
                {
                    IsAttended = true;
                }

                return true;
            }
            return false;
        }

        /// <summary>
        /// Clear all classes.
        /// </summary>
        public void ClearClasses()
        {
            foreach (var cl in m_classes)
            {
                cl.PropertyChanged -= OnClassPropertyChanges;
            }

            m_classes.Clear();
            IsAttended = false;
        }

        public void OnClassPropertyChanges(Object s, PropertyChangedEventArgs args)
        {
            Class cl = s as Class;
            if (cl == null)
            {
                return;
            }

            //if class became "attended"
            if (cl.CurrentStatus == ClassStatus.Attended)
            {
                IsAttended = true;
            }            
            else
            {
                //we can't really know did this class have "attended"
                //status before or no. If it had and was the only 
                //one that had this status before this event, than now theme is
                //supposed to change it's IsAttended status to false.
                //So we need to fully refresh IsAttended status.
                if (IsAttended)
                {
                    bool hasAttended = Classes.Any((c) => c.CurrentStatus == ClassStatus.Attended);
                    IsAttended = hasAttended;
                }
            }
        }

        bool m_attended = false;
        public virtual bool IsAttended
        {
            get
            {
                return m_attended;
            }
            private set
            {
                if (m_attended != value)
                {
                    m_attended = value;
                    OnPropertyChanged("IsAttended");
                }
            }
        }

        [OnDeserialized]
        void OnDeserialized(StreamingContext context)
        {
            m_classes.OnDeserialization(this);
                        
            foreach (Class cl in m_classes)
            {
                cl.PropertyChanged -= OnClassPropertyChanges;
                cl.PropertyChanged += OnClassPropertyChanges;
            }
        }


        [field:NonSerialized]
        public event PropertyChangedEventHandler PropertyChanged;
        
        protected virtual void OnPropertyChanged(string name)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(name));
            }        
        }
    }
}
